Fix disabled endpoints for vhosts not being reflected correctly#1190
Fix disabled endpoints for vhosts not being reflected correctly#1190
Conversation
WalkthroughAdds reusable vhost and endpoint helper functions, refactors DevPortal and Publisher components to use them, and updates UI to disable or hide invalid server/host options and adapt endpoint selection logic. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Poem
Pre-merge checks and finishing touches✅ Passed checks (5 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/DeploymentOnbording.jsx (1)
232-237: Default vhost selection can pick WS‑disabled vhosts for WebSocket APIsFor WS APIs,
defaultVhostsalways usese.vhosts[0]viagetHostValue(e.vhosts[0], api.isWebSocket()). If the first vhost has WS endpoints disabled while a later vhost has them enabled,hasValidHosts(row)will still be true (because some vhost is valid), but the default selection for that env will point at the wrong vhost and be used increateDeployRevision.To avoid deploying a WS API to a vhost where WS is disabled, base the default host on the first vhost that actually has valid WS endpoints when
api.isWebSocket()is true.- const defaultVhosts = selectedInternalGateways.map((e) => { - if (e.vhosts && e.vhosts.length > 0) { - return { - env: e.name, - vhost: getHostValue(e.vhosts[0], api.isWebSocket()), - }; - } else { - return undefined; - } - }); + const defaultVhosts = selectedInternalGateways.map((e) => { + if (!e.vhosts || e.vhosts.length === 0) { + return undefined; + } + const candidates = api.isWebSocket() + ? e.vhosts.filter((v) => hasValidWebSocketPorts(v)) + : e.vhosts; + const first = candidates[0]; + return first + ? { env: e.name, vhost: getHostValue(first, api.isWebSocket()) } + : undefined; + }); @@ - const defaultVhosts = selectedExternalGateways.map((e) => { - if (e.vhosts && e.vhosts.length > 0) { - return { - env: e.name, - vhost: getHostValue(e.vhosts[0], api.isWebSocket()), - }; - } else { - return undefined; - } - }); + const defaultVhosts = selectedExternalGateways.map((e) => { + if (!e.vhosts || e.vhosts.length === 0) { + return undefined; + } + const candidates = api.isWebSocket() + ? e.vhosts.filter((v) => hasValidWebSocketPorts(v)) + : e.vhosts; + const first = candidates[0]; + return first + ? { env: e.name, vhost: getHostValue(first, api.isWebSocket()) } + : undefined; + });Also applies to: 253-258
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/Environments.jsx (1)
608-619: Default vhost selection for WS APIs can still pick a vhost with WS disabledAs in
DeploymentOnbording.jsx,defaultVhostshere always usese.vhosts[0]and wraps it withgetHostValue(e.vhosts[0], api.isWebSocket()). For WS APIs, if the first vhost has WS endpoints disabled but a later vhost has them enabled,hasValidHosts(e)will be true, yet the default selection will target the WS‑disabled vhost and be used in deployments.This undermines the intent of the new validity checks.
Refine the default selection to prefer a vhost with valid WS endpoints for WS APIs:
- const defaultVhosts = selectedInternalGateways.map((e) => { - if (e.vhosts && e.vhosts.length > 0) { - return { - env: e.name, - vhost: getHostValue(e.vhosts[0], api.isWebSocket()) - }; - } else { - return undefined; - } - }); + const defaultVhosts = selectedInternalGateways.map((e) => { + if (!e.vhosts || e.vhosts.length === 0) { + return undefined; + } + const candidates = api.isWebSocket() + ? e.vhosts.filter((v) => hasValidWebSocketPorts(v)) + : e.vhosts; + const first = candidates[0]; + return first + ? { env: e.name, vhost: getHostValue(first, api.isWebSocket()) } + : undefined; + }); @@ - const defaultVhosts = selectedExternalGateways.map((e) => { - if (e.vhosts && e.vhosts.length > 0) { - return { - env: e.name, - vhost: getHostValue(e.vhosts[0], api.isWebSocket()) - }; - } else { - return undefined; - } - }); + const defaultVhosts = selectedExternalGateways.map((e) => { + if (!e.vhosts || e.vhosts.length === 0) { + return undefined; + } + const candidates = api.isWebSocket() + ? e.vhosts.filter((v) => hasValidWebSocketPorts(v)) + : e.vhosts; + const first = candidates[0]; + return first + ? { env: e.name, vhost: getHostValue(first, api.isWebSocket()) } + : undefined; + });This keeps default behavior unchanged for HTTP APIs, while ensuring WS APIs default to a vhost where WS is actually enabled.
Also applies to: 631-642
🧹 Nitpick comments (5)
portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/AsyncApiConsole/AsyncApiUI.jsx (2)
57-62: Disabled styling looks fine; consider using theme palette for consistencyThe
.Mui-disabledstyling on the Select matches the intended UX and is scoped correctly to.selectList. If you want this to stay in sync with the app theme, you could optionally refactorRootto take({ theme })and usetheme.palette.action.disabledBackground/theme.palette.text.disabledinstead of hard‑coded colors, but it’s not required for this PR.
78-90: Centralized endpoint selection is good; double‑check preference for insecure vs secure URLs
getPreferredEndpointnicely centralizes the initial endpoint selection and theuseEffectkeepsendPointin sync whenURLsorapi.typechange, which is a clear improvement.One thing to verify: the helper prefers non‑secure schemes when both are present:
- For WS APIs, it picks
wsfirst and only falls back towss.- For non‑WS APIs, it picks
httpfirst and only falls back tohttps.If both secure and insecure endpoints are configured, this will always pick the insecure one. That may be intentional (e.g., for local/dev usage), but if the desired behavior is to prefer secure endpoints when available, you may want to invert the order:
- if (apiType === CONSTANTS.API_TYPES.WS) { - // prefer ws, but if ws does not exist fall back to wss - if (URLsObj.ws) return URLsObj.ws; - if (URLsObj.wss) return URLsObj.wss; + if (apiType === CONSTANTS.API_TYPES.WS) { + // Prefer secure wss, fall back to ws if wss is not available + if (URLsObj.wss) return URLsObj.wss; + if (URLsObj.ws) return URLsObj.ws; } - // for non-WS APIs prefer http, but if http does not exist fall back to https - if (URLsObj.http) return URLsObj.http; - if (URLsObj.https) return URLsObj.https; + // For non-WS APIs prefer https, fall back to http if https is not available + if (URLsObj.https) return URLsObj.https; + if (URLsObj.http) return URLsObj.http;Please confirm which ordering matches the product expectation before changing.
Also applies to: 92-92, 103-105
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/DeploymentOnbording.jsx (1)
175-195: Shared WS/host helper logic looks good, but consider centralizingThe wsDisabled/wssDisabled/hasValidWebSocketPorts/hasValidHosts/getHostValue helpers are clean and match those in
Environments.jsx, which is good for consistency. Given they are duplicated across components, consider extracting them into a shared utility to avoid drift on future changes.portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/Environments.jsx (2)
550-574: WS/host helper set is consistent and readableThe wsDisabled/wssDisabled/hasValidWebSocketPorts/hasValidHosts/getHostValue helpers cleanly encapsulate WS vs HTTP host/port validity and selection, and they align with the same logic reused in
DeploymentOnbording.jsx.Consider moving these helpers to a shared module so
Environments.jsxandDeploymentOnbording.jsxcan import them from a single place.
2551-2555: getVhostHelperText and tooltips now communicate host availability clearlyThe WS branch of
getVhostHelperTextcorrectly resolves the selected vhost viawsHost/wssHostand returns a clear “No valid hosts available for this environment” message when WS endpoints are disabled on that vhost. Combined with the updated tooltips that fall back to that message when!hasValidHosts(row), this should make host availability much more transparent to users.Also applies to: 2568-2574, 3567-3575, 3795-3803
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/AsyncApiConsole/AsyncApiUI.jsx(4 hunks)portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/Environments.jsx(10 hunks)portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/DeploymentOnbording.jsx(7 hunks)portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/Environments.jsx(18 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/AsyncApiConsole/AsyncApiUI.jsx (1)
portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/AsyncApiConsole/AsyncApiConsole.jsx (1)
URLs(91-91)
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/DeploymentOnbording.jsx (1)
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/Environments.jsx (5)
wsDisabled(550-550)wssDisabled(551-551)hasValidWebSocketPorts(553-555)hasValidHosts(557-563)getHostValue(566-574)
🔇 Additional comments (5)
portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/AsyncApiConsole/AsyncApiUI.jsx (1)
245-245: Guard against falsyURLswhen usingObject.keys/Object.valuesThe disable logic assumes
URLsis always a non‑null object:disabled={Object.keys(URLs).length === 0 || !Object.values(URLs).some(Boolean)} {Object.keys(URLs).length === 0 || !Object.values(URLs).some(Boolean) ? ( ... )}If
URLstransitions tonull/undefinedduring async environment loading,Object.keys()andObject.values()will throw. Add a falsy check:-disabled={Object.keys(URLs).length === 0 || !Object.values(URLs).some(Boolean)} +disabled={!URLs || Object.keys(URLs).length === 0 || !Object.values(URLs).some(Boolean)}Additionally, if
URLscan contain non-string values (e.g., nested objects or metadata), the MenuItems will render as[object Object]. Filter to string values only:Object.entries(URLs) .filter(([, value]) => typeof value === 'string' && value) .map(([key, value]) => ( <MenuItem value={value} key={key}>{value}</MenuItem> ));Applies to lines 245 and 248–259.
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/DeploymentOnbording.jsx (1)
459-460: Host validity checks and vhost dropdown filtering are aligned with WS semanticsDisabling environment selection (
Checkbox) and the vhostTextFieldwhen!hasValidHosts(row), and filtering vhosts for WS APIs viahasValidWebSocketPorts(vhost)before mapping toMenuItems, correctly prevents choosing vhosts with WS endpoints disabled. UsinggetHostValuefor both key and value keeps the stored value consistent for HTTP vs WS cases.Also applies to: 550-565, 684-686, 748-762
portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/Environments.jsx (2)
121-130: pickFirstEnabledUrl helper is simple and robustThe helper defensively handles missing/invalid
urlsand cleanly picks the first non-empty string, which keeps the call sites concise.
502-518: selectedEndpoint propTypes shape is now explicit and matches usageThe new
selectedEndpointshape (environmentName/Type plus nestedURLsanddefaultVersionURLs) matches how the component reads these fields and should help catch mismatches at runtime.portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/Environments.jsx (1)
795-803: Guard in handleChange correctly prevents selecting environments without valid hostsThe early return when
event.target.checked && environment && !hasValidHosts(environment)prevents adding environments that have no valid hosts for the current API type intoSelectedEnvironment, which complements the UIdisabledstates. This is a good safety net around the selection logic.
portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/Environments.jsx
Show resolved
Hide resolved
...blisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/Environments.jsx
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/DeploymentOnbording.jsx (1)
215-241: For WS APIs, default vhost should prefer the first WS‑valid vhost, not always index 0You now store host strings via
getHostValue(e.vhosts[0], api.isWebSocket()). For WebSocket APIs this still blindly uses the first vhost even if it has WS endpoints disabled while a later vhost is WS‑enabled. In that case:
hasValidHosts(row, api.isWebSocket())will enable the environment (because at least one vhost is valid),- but
selectedVhostDeploywill still hold the disabled vhost’s host value until the user manually changes it.Safer to choose the first WS‑valid vhost when
api.isWebSocket():- const defaultVhosts = selectedInternalGateways.map((e) => { - if (e.vhosts && e.vhosts.length > 0) { - return { - env: e.name, - vhost: getHostValue(e.vhosts[0], api.isWebSocket()), - }; - } else { - return undefined; - } - }); + const defaultVhosts = selectedInternalGateways.map((e) => { + if (!e.vhosts || e.vhosts.length === 0) { + return undefined; + } + const baseVhost = api.isWebSocket() + ? (e.vhosts.find((v) => hasValidWebSocketPorts(v)) || e.vhosts[0]) + : e.vhosts[0]; + return { + env: e.name, + vhost: getHostValue(baseVhost, api.isWebSocket()), + }; + });Apply the same pattern in the external‑gateway branch below for consistency.
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/Environments.jsx (1)
590-600: Mirror “choose first WS‑valid vhost” here for consistency with onboardingAs in
DeploymentOnbording.jsx, default vhosts for each environment are still based one.vhosts[0], now wrapped ingetHostValue. For WS APIs this can select a WS‑disabled vhost even when a later vhost is WS‑enabled, leavingselectedVhosts/selectedVhostDeploypointing at a disabled host until the user changes it.To keep defaults aligned with your new validity helpers and avoid that subtle mismatch, consider:
- const defaultVhosts = selectedInternalGateways.map((e) => { - if (e.vhosts && e.vhosts.length > 0) { - return { - env: e.name, - vhost: getHostValue(e.vhosts[0], api.isWebSocket()) - }; - } else { - return undefined; - } - }); + const defaultVhosts = selectedInternalGateways.map((e) => { + if (!e.vhosts || e.vhosts.length === 0) { + return undefined; + } + const baseVhost = api.isWebSocket() + ? (e.vhosts.find((v) => hasValidWebSocketPorts(v)) || e.vhosts[0]) + : e.vhosts[0]; + return { + env: e.name, + vhost: getHostValue(baseVhost, api.isWebSocket()), + }; + });and similarly in the external‑gateway branch below.
Also applies to: 613-625
♻️ Duplicate comments (3)
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/Environments.jsx (3)
3161-3170: External gateways in deploy‑new‑revision now usegetHostValueconsistentlyThe external gateways’ vhost dropdown now derives key/value/label via
getHostValue(vhost, api.isWebSocket()), so HTTP vs WS host selection is centralized and consistent with internal gateways and other views.Given earlier discussion that fully filtering out WS‑disabled vhosts in some edge‑case UIs is acceptable to skip, leaving this menu unfiltered and just normalizing the host values is reasonable.
3551-3603: Internal “Select Access URL” dropdown: tooltip/disabled state respect host validity, options normalizedFor internal gateways with no deployed revision:
- Tooltip now shows either the usual helper text or a clear
'No valid hosts available for this environment'whenhasValidHosts(row, api.isWebSocket())is false.- The vhost selector gets disabled under the same condition, preventing WS APIs from picking a vhost in envs that have no WS‑capable hosts at all.
- Option values are now generated via
getHostValue, so the selected value matches the same host string used everywhere else.Previously raised concerns about additionally filtering out WS‑disabled vhosts in this dropdown were discussed and accepted as an edge case; the current normalization is consistent with that decision.
3779-3827: External “Select Access URL” dropdown mirrors internal behavior and usesgetHostValueThe external gateways table now:
- Uses the same “No valid hosts available for this environment” tooltip when
hasValidHosts(row, api.isWebSocket())is false.- Disables the vhost selector under that condition.
- Normalizes option values/labels via
getHostValue(vhost, api.isWebSocket()).That brings external gateways in line with internal ones while preserving the earlier choice not to hard‑filter WS‑disabled vhosts in this particular view.
🧹 Nitpick comments (3)
portals/publisher/src/main/webapp/source/src/app/components/Shared/Environments/Vhosts.jsx (1)
34-55: Host validity / selection helpers look consistent with API‑type semantics
hasValidHostsonly enforces WebSocket vhost validity whenisWebSocket === true, leaving HTTP and GraphQL APIs to accept any defined vhost, which matches typical expectations (HTTP always available; WS is only mandatory for pure WS APIs).getHostValue’s “prefer wsHost, fall back to wssHost when ws is disabled” is internally consistent with how you filter vhosts elsewhere.If, in future, you decide secure WS (
wssHost) should be preferred when both WS/WSS are enabled, this is the single place to flip that policy. For now, the current behavior is coherent with the rest of the PR.portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/Environments.jsx (2)
777-795: Env selection short‑circuit on invalid hosts is safe but slightly redundantThe new check in
handleChange:const environment = settings.environment.find((env) => env.name === event.target.value); if (event.target.checked && environment && !hasValidHosts(environment, api.isWebSocket())) { return; }correctly prevents adding environments that don’t have any valid hosts for a WS API, even if something went wrong with
disabledprops. It’s a harmless extra guard, and theevent.target.checkedpredicate ensures text fields (like description) are unaffected.
2526-2556:getVhostHelperTextWS/shortened output is improved; will benefit from the null‑guard onhasValidWebSocketPortsChanges here:
- For WS APIs, you now resolve the underlying vhost via
wsHost/wssHostand short‑circuit with'No valid hosts available for this environment'whenhasValidWebSocketPorts(vhost)is false.- In
shortenmode you compute a single preferred URL (secondary || primary) and truncate it, rather than showing the concatenatedcombinedstring, which is clearer.- GraphQL handling (HTTP + WS URLs) remains intact and only applies when not shortening.
Once
hasValidWebSocketPortstreatsnull/undefinedas “no valid ports” (see earlier comment), this helper will also behave sensibly if a selection ever points at a non‑existent vhost.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/DeploymentOnbording.jsx(7 hunks)portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/Environments.jsx(18 hunks)portals/publisher/src/main/webapp/source/src/app/components/Shared/Environments/Vhosts.jsx(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-08T04:10:03.206Z
Learnt from: DDH13
Repo: wso2/apim-apps PR: 1190
File: portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/Environments.jsx:140-152
Timestamp: 2025-12-08T04:10:03.206Z
Learning: For WSO2 APIM devportal, the backend populates selectedEndpoint.URLs based on API type: HTTP APIs have only http/https populated (ws/wss are null), WebSocket APIs have only ws/wss populated (http/https are null), and GraphQL APIs have all four protocols (http, https, ws, wss) populated.
Applied to files:
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/Environments.jsx
🧬 Code graph analysis (2)
portals/publisher/src/main/webapp/source/src/app/components/Shared/Environments/Vhosts.jsx (2)
portals/devportal/src/main/webapp/source/src/app/components/Applications/Details/Overview.jsx (1)
environment(204-204)portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/StreamingAPI/APICreateStreamingAPI.jsx (1)
isWebSocket(79-79)
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/DeploymentOnbording.jsx (1)
portals/publisher/src/main/webapp/source/src/app/components/Shared/Environments/Vhosts.jsx (6)
getHostValue(47-55)getHostValue(47-55)hasValidHosts(34-39)hasValidHosts(34-39)hasValidWebSocketPorts(24-26)hasValidWebSocketPorts(24-26)
🔇 Additional comments (6)
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/DeploymentOnbording.jsx (2)
419-451: WS vhost filtering and env disabling are wired correctly
- Internal gateway checkbox:
disabled={!hasValidHosts(row, api.isWebSocket())}cleanly blocks selecting environments that have no WS‑capable vhost for WS APIs while remaining a no‑op for HTTP/GraphQL.- Internal and external vhost dropdowns now filter with
hasValidWebSocketPortswhenapi.isWebSocket()and usegetHostValuefor key/value/display, so WS APIs cannot pick a WS‑disabled vhost from these menus.This aligns with the PR’s objective of reflecting disabled endpoints in the UI and centralizes the logic through the shared helpers.
Also applies to: 484-549
663-669: External gateway checkbox + dropdown behavior is consistent and safeThe external gateway checkbox is now disabled when either deployments are restricted or
!hasValidHosts(row, api.isWebSocket()), and the vhost dropdown options for WS APIs are filtered byhasValidWebSocketPortswith host values derived viagetHostValue.That keeps WS selection consistent between internal and external gateways and prevents choosing WS‑disabled vhosts through this onboarding flow.
Also applies to: 706-747
portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/Environments.jsx (4)
77-83: Centralized VHost helper imports are appropriateImporting
wsDisabled,wssDisabled,hasValidWebSocketPorts,hasValidHosts, andgetHostValuehere keeps the environment logic consistent with the shared helpers and avoids duplicating WS/HTTP host rules in this large component.
2116-2145: WS URL generation now respects disabled WS/WSS endpointsThe
getGatewayAccessUrlWS branch now only buildsws://andwss://URLs whenwsDisabled(vhost)/wssDisabled(vhost)are false. That prevents bogusws://null:null/wss://null:nullURLs from appearing when an endpoint is disabled, while still returning empty strings for missing variants.Combined with your validation helpers, this should give more accurate Gateway Access URLs in the tables and helper text.
2184-2188: Revision selection correctly gated on host validityBoth “no revision yet” and “some revisions available” flows now disable the revision
<TextField>when!hasValidHosts(row, api.isWebSocket()). This:
- Blocks deploy attempts for WS APIs on environments with no WS‑capable vhosts.
- Leaves HTTP/GraphQL behavior unchanged (any env with vhosts remains selectable).
That’s consistent with the rest of your host validation and the PR goal.
Also applies to: 2298-2302
2894-2896: Deploy‑new‑revision internal gateway card: WS validation and option list look goodInside the deploy‑new‑revision dialog:
- The internal gateway checkbox is now disabled when
!hasValidHosts(row, api.isWebSocket()), mirroring the main environments list and preventing WS APIs from targeting envs without WS‑capable vhosts.- The vhost selector is disabled under the same condition and its options are filtered by
hasValidWebSocketPortsfor WS APIs, with display/value coming fromgetHostValue.This keeps the dialog behavior aligned with the onboarding component and ensures WS‑disabled vhosts can’t be selected here.
Also applies to: 2972-2995
portals/publisher/src/main/webapp/source/src/app/components/Shared/Environments/Vhosts.jsx
Show resolved
Hide resolved
|
…development iteration


Fixes: wso2/api-manager#4234
Bug Fixes
UX Improvements
Summary by CodeRabbit
Release Notes
New Features
Bug Fixes
UI/UX
✏️ Tip: You can customize this high-level summary in your review settings.